import cv2
import numpy as np

img = cv2.imread('../images/card.png')

# 一、轮廓检测与绘制
# 1. 灰度化
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# 2. 二值化
ret, img_binary = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY_INV | cv2.THRESH_OTSU)

# 3. 高斯滤波
img_gaussian = cv2.GaussianBlur(img_binary, ksize=(5, 5), sigmaX=3)

# 4. 其实在这里还应该做一次形态学处理，开运算和闭运算

# 5. 寻找轮廓
contours, hierarchy = cv2.findContours(  # 返回轮廓和层次结构
    img_gaussian,
    mode=cv2.RETR_LIST,   # 轮廓检索模式
    method=cv2.CHAIN_APPROX_SIMPLE  # 轮廓近似方式
)

# 返回的轮廓是一个列表，列表中的元素是轮廓点集
print("轮廓数量：", len(contours))
for i, c in enumerate(contours):
    print(f"第{i+1}个轮廓的边界点的个位数为{len(c)}")

# 6. 绘制轮廓
# 一般不建议在原图上直接绘制轮廓，因为轮廓的绘制会改变原图的内容，建议拷贝一份图片绘制轮廓
img_copy = img.copy()
cv2.drawContours(
    img_copy,
    contours,  # 要绘制的轮廓列表
    contourIdx=-1,  # 轮廓的索引，-1表示绘制所有轮廓
    color=(0, 0, 255),
    thickness=2
)

# 二、轮廓特征分析
# 7. 分析轮廓的凸包特征
hull = cv2.convexHull(
    contours[26]  # 凸包对应的轮廓, 这里取第27个轮廓
)
print("凸包顶点数量：", len(hull))

# 8. 绘制凸包
cv2.polylines(
    img_copy,
    [hull],
    isClosed=True,
    color=(255, 0, 0),
    thickness=2
)

# 9.1 外接矩形 (不考虑旋转，矩形的边与坐标轴是平行的)
x, y, w, h = cv2.boundingRect(hull)
cv2.rectangle(
    img_copy,
    pt1=(x, y),
    pt2=(x+w, y+h),
    color=(0, 255, 0),
    thickness=2
)
# 9.2 最小外接矩形 (考虑旋转，矩形的边与坐标轴不一定是平行的)
# 返回三元组--矩形的中心点、宽高、旋转角度
ret = cv2.minAreaRect(hull)
# 因为绘制矩形需要的参数为 左上角和右下角坐标，而我们的返回值是（中心点坐标、宽度高度、旋转角度）
# 虽然通过数学计算能算出来，但是比较复杂，所以我们不采用绘制矩形的方式来绘制最小外接矩形，而是采用绘制轮廓的方式
# 补充一个函数    cv2.boxPoints() 可获取旋转矩形的四个顶点
box_points = np.int32(cv2.boxPoints(ret))  # 注意转换数据类型
cv2.drawContours(
    img_copy,
    [box_points],
    contourIdx=-1,
    color=(255, 255, 0),
    thickness=2
)
# 9.3 最小外接圆
(x, y), radius = cv2.minEnclosingCircle(hull)
cv2.circle(
    img_copy,
    center=(int(x), int(y)),   # 注意转换数据类型
    radius=int(radius),
    color=(0, 255, 255),
    thickness=2
)

cv2.imshow("img", img_copy)
cv2.waitKey(0)